package paramedic;

import java.util.ArrayList;
import java.util.List;

import path.Path;
import path.PathTest;

import moves.MovementSender;
import moves.Movement.Direction;
import evacuator.MovementRely;
import agents.UtilsAgents;
import building.Cell;
import building.InfoAgent;
import building.InfoGame;
import jade.core.AID;
import jade.core.Agent;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.FailureException;
import jade.domain.FIPAAgentManagement.NotUnderstoodException;
import jade.domain.FIPAAgentManagement.RefuseException;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.lang.acl.UnreadableException;
import jade.proto.AchieveREResponder;

public class ParamedicAgent extends Agent {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private InfoGame game;
	private List<Cell> injuredCivilian;
	private MovementSender ms;
	private boolean accepted;
	private InfoAgent infoAgent;
	private int my_x;
	private int my_y;
	private int my_f;
	private Path short_path;
	private Direction step;
	private boolean freeAgent;

	protected void setup() {
		System.out.println(this.getLocalName() + " is ready.");

		// Register the agent to the DF
		ServiceDescription sd1 = new ServiceDescription();
		sd1.setType(UtilsAgents.PARAMEDIC_AGENT);
		sd1.setName(getLocalName());
		sd1.setOwnership(UtilsAgents.OWNER);
		DFAgentDescription dfd = new DFAgentDescription();
		dfd.addServices(sd1);
		dfd.setName(getAID());
		try {
			DFService.register(this, dfd);
			// showMessage("Registered to the DF");
			System.out.println("Registered to the DF");
		} catch (FIPAException e) {
			System.err.println(getLocalName() + " registration with DF "
					+ "unsucceeded. Reason: " + e.getMessage());
			doDelete();
		}

		// Add a Behavior to receive Turns
		MessageTemplate mt = MessageTemplate
				.MatchProtocol(agents.UtilsAgents.PROTOCOL_TURN);
		this.addBehaviour(new QueriesReceiver(this, mt));

		ms = new MovementSender(this, this.getAID(),
				agents.UtilsAgents.COORDINATOR_AGENT);

		injuredCivilian = new ArrayList<Cell>();
		super.setup();
	}

	class QueriesReceiver extends AchieveREResponder {

		private InfoGame game;
		private Cell cell;
		private AID agentInPanic;
		private int currFloor;
		private int countInjured;
		private AID agentInjured;

		@Override
		protected ACLMessage prepareResponse(ACLMessage arg0)
				throws NotUnderstoodException, RefuseException {
			return null;
		}

		@Override
		protected ACLMessage prepareResultNotification(ACLMessage arg0,
				ACLMessage arg1) throws FailureException {
			return null;
		}

		protected ACLMessage handleRequest(ACLMessage arg0)
				throws NotUnderstoodException, RefuseException {
			try {
				Object objectReceived = arg0.getContentObject();

				if (objectReceived instanceof InfoGame) {
					// Is the coordinator informing of a new turn
					game = (InfoGame) objectReceived;
					/*
					 * if (!game.isRemoved(this.myAgent.getAID())){
					 * 
					 * }
					 */

					showMessage("Paramedic: New turn received from coordinator: "
							+ this.myAgent.getLocalName()
							+ game.getInfo().getTurn());

					Cell paramedic_cel = UtilsAgents.findAgent(
							myAgent.getAID(), game);
					InfoAgent oldInfoAgent = paramedic_cel.getAgent();

					// oldInfoAgent.setLastTurn(game.getInfo().getTurn());

					updateParamedic_byInf(oldInfoAgent);
					injuredCivilian = game
							.getCivilianInjuredPosition(paramedic_cel);
					// Find all my agents and send them the new turn
					ACLMessage message = new ACLMessage(ACLMessage.REQUEST);
					// for(int f=0;f<game.getMap().length;f++)
					Cell temp = agents.UtilsAgents.findAgent(
							this.myAgent.getAID(), game);

					countInjured = 0;

					// try{
					if (temp != (null)) {
						currFloor = temp.getFloor();
						for (int r = 0; r < game.getMap().length; r++)
							for (int c = 0; c < game.getMap().width; c++) {
								Cell cell = game.getCell(r, c, currFloor);
								if (cell != null)
									if (cell.isThereAnAgent()) {
										InfoAgent a = cell.getAgent();
										if (a.getAgent().equals("C")) {

											if (a.isInjured()) {
												countInjured++;
											}

										}
									}

							}// for

						if (temp == null)
							System.out.println("no encontrada la ubivacion"
									+ this.myAgent);

						// countCivilians = countCivilians - countInjured -
						// countPanic - countDied-countBlocked;
						System.out.println(this.myAgent.getLocalName()
								+ " countInjured" + countInjured);

						if (countInjured == 0) {
							System.out
									.println("no more civilians injured in my floor"
											+ myAgent.getLocalName()/* + content */);
							Cell stairs = UtilsAgents.getStairs(game);
							changeFloor(game, stairs);

						}

						else {
							if (countInjured != 0) {

								Cell injuredC = injuredCivilian.get(0);
								agentInjured = cureCivilians(game, injuredC);
								if (agentInjured != null) {
									game.getCivilianInjuredPosition(
											paramedic_cel).remove(0);
									injuredC.getAgent().injured = false;
								}
							}

						}
					}// if

				}// if

			} catch (UnreadableException e) {
				e.printStackTrace();
				showMessage("Received an Object that cannot be understood");
			} catch (Exception e) {
				e.printStackTrace();
			}

			return super.handleRequest(arg0);

		}

		private void updateParamedic_byInf(InfoAgent oldInfoAgent) {
			// TODO Auto-generated method stub

		}

		private void showMessage(String string) {
			// TODO Auto-generated method stub
			System.out.println(string);
		}

		/**
	        	 *
	        	 */
		private static final long serialVersionUID = -6176920249600913698L;

		public QueriesReceiver(Agent arg0, MessageTemplate arg1) {
			super(arg0, arg1);
		}
	}

	private void changeFloor(InfoGame game2, Cell s) {
		System.err.println("change Floor ");
		Cell stairs = s;
		accepted = false;
		int distance = -1;

		try {
			infoAgent = agents.UtilsAgents.findAgent(this.getAID(), game2)
					.getAgent();
			Cell varAgent = agents.UtilsAgents.findAgent(this.getAID(), game2);
			System.out.println("Agent " + infoAgent.getAID() + "in position"
					+ varAgent);
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			System.out.println("change floor  " + e1);
		}
		Cell begin = new Cell(Cell.FREE);

		my_x = agents.UtilsAgents.findAgent(this.getAID(), game2).getColumn();
		my_y = agents.UtilsAgents.findAgent(this.getAID(), game2).getRow();
		my_f = agents.UtilsAgents.findAgent(this.getAID(), game2).getFloor();

		System.out.println("CONTENT Param " + stairs);
		if (stairs != null) {

			try {

				distance = evaluateAction(stairs);
				System.out.println("Paramedic : current position" + my_f + "x"
						+ my_x + "y" + my_y);
				begin.setColumn(my_x);
				begin.setRow(my_y);
				begin.setFloor(my_f);

				System.out.println("Paramedic :distancia calculada por"
						+ this.getLocalName() + ":" + distance + "in floor"
						+ my_f);
			} catch (Exception e) {

				System.out.println("Paramedic exception change floor "
						+ e.toString());
			}
			if (distance == 1) {

				try {
					if (stairs.isThereAnAgent()) {
						// ms.go(Direction.NOP);
						System.out.println("Paramedic: un agente nelle scale"
								+ stairs + stairs.getAgent());
					} else {
						// protocolSendFinishDownload.addBehaviour(myAgent);
						System.out.println(this.getLocalName()
								+ "le scale sono libere");
						// freeAgent= true;
						// accepted=false;
						step = getNextStep();
						// System.out.println(myAgent.getLocalName() + "step11"
						// + getNextStepDesti(content));
						ms.go(step);
						freeAgent = true;
						accepted = false;
						return;

					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}// if
			else if (distance == 0) {

				System.err.println("Scale!");
				Cell door = stairs;
				door.setRow(0);
				door.setFloor(stairs.getFloor() + 1);
				System.out.println("is there an agent in the door?" + door
						+ " " + door.isThereAnAgent());
				if (door.isThereAnAgent()) {
					// ms.go(Direction.NOP);
					System.out.println("hay un agente en las escaleras" + door
							+ door.getAgent());

					// return;
				}

				// finishDownload.addBehaviour(this);

				// notifyFinishedLoad(infoGame);
				step = getNextStepDesti(door);
				System.out.println("paso al siguiente piano"
						+ this.getLocalName());
				ms.go_floor(step);
				freeAgent = true;
				accepted = false;

				return;

			} else if (distance == 2) {

				step = getNextStep();
				System.out
						.println("Paramedic : sono a 2 passi dal civile feirto"
								+ this.getLocalName());

				ms.go(step);
				freeAgent = false;
				accepted = true;
				return;

			}// if

			else if (distance > 2)

			{
				System.out
						.println("Paramedic :sto a piu di 2 passi dal civile "
								+ this.getLocalName() + "destination cell R"
								+ stairs.getRow() + " C" + stairs.getColumn());
				distance = evaluateAction(stairs);
				try {
					step = getNextStep();
				} catch (Exception e) {
					System.err
							.println("Paramedic: non ho potuto calcolare il prossimo step ");
				}
				System.out.println("Paramedic step: " + step);

				ms.go(step);
				freeAgent = false;
				accepted = true;

			}
		} else
			System.out.println("Param Stairs not found :( ");
	}

	public AID cureCivilians(InfoGame infoGame, Cell panicC) {
		System.err.println("Cure civilians ");
		this.game = infoGame;
		accepted = false;
		int distance = -1;
		Cell myContent = panicC;
		try {
			infoAgent = agents.UtilsAgents.findAgent(this.getAID(), infoGame)
					.getAgent();
			Cell varAgent = agents.UtilsAgents.findAgent(this.getAID(),
					infoGame);
			System.out.println("Agent " + infoAgent.getAID() + "in position"
					+ varAgent);
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			System.out.println("Cure civilians init " + e1);
		}
		Cell begin = new Cell(Cell.FREE);

		my_x = agents.UtilsAgents.findAgent(this.getAID(), infoGame)
				.getColumn();
		my_y = agents.UtilsAgents.findAgent(this.getAID(), infoGame).getRow();
		my_f = agents.UtilsAgents.findAgent(this.getAID(), infoGame).getFloor();

		System.out.println("CONTENT Param " + myContent);
		if (myContent != null) {

			try {

				distance = evaluateAction(myContent);
				System.out.println("Paramedic : current position" + my_f + "x"
						+ my_x + "y" + my_y);
				begin.setColumn(my_x);
				begin.setRow(my_y);
				begin.setFloor(my_f);
				System.out.println("Paramedic :distancia calculada por"
						+ this.getLocalName() + ":" + distance + "in floor"
						+ my_f);
			} catch (Exception e) {

				System.out.println("Paramedic exception helpCivilians "
						+ e.toString());
			}

			// look at the perimeter
			if (distance == 1) {
				System.out.println("Paramedic distancia 1 ");

				System.out.println("Caso 1");
				step = getNextStep();
				InfoAgent ip = panicC.getAgent();
				System.out.println("Paramedic " + this.getLocalName()
						+ ":sono dal ferito " + ip.getAID().getLocalName());

				ip.injured = false;
				System.out.println("CureCivilian: "
						+ ip.getAID().getLocalName() + " is not injured.");
				System.err.println(ip.getAID().getLocalName()
						+ "  set injured=false -> " + ip.injured);

				ms.cure(step);
				freeAgent = true;
				accepted = false;

				// notifyFinishedLoad(this.game);
				return ip.getAID();
			}

			else if (distance == 2) {

				step = getNextStep();
				System.out
						.println("Paramedic : sono a 2 passi dal civile feirto"
								+ this.getLocalName());

				ms.go(step);
				freeAgent = false;
				accepted = true;
				return null;

			}// if

			else if (distance > 2)

			{
				System.out
						.println("Paramedic :sto a piu di 2 passi dal civile "
								+ this.getLocalName() + "destination cell R"
								+ myContent.getRow() + " C"
								+ myContent.getColumn());
				distance = evaluateAction(myContent);
				try {
					step = getNextStep();
				} catch (Exception e) {
					System.err
							.println("Paramedic: non ho potuto calcolare il prossimo step ");
				}
				System.out.println("Paramedic step: " + step);

				ms.go(step);
				freeAgent = false;

			} // else

			else {
				System.out.println("Paramedic null path" + this.getLocalName());
			}

		} else
			System.out.println("Paramedic: content null" + distance);
		return null;

	}

	/**
	 * 
	 * @param cell
	 * @return the best distance for agent to go a one cell
	 */
	private int evaluateAction(Cell cell) {

		int xfinal = cell.getColumn();
		int yfinal = cell.getRow();
		int zfinal = cell.getFloor();
		// return the short path
		// Search my position
		my_x = agents.UtilsAgents.findAgent(this.getAID(), game).getColumn();
		my_y = agents.UtilsAgents.findAgent(this.getAID(), game).getRow();
		my_f = agents.UtilsAgents.findAgent(this.getAID(), game).getFloor();

		if (zfinal == my_f) {
			PathTest test = new PathTest(game);

			System.out.println("*****Protocol Paramedic evaluate action"
					+ zfinal);
			System.out.println(this.getAID() + "Im in floor" + my_f
					+ "destination" + zfinal);
			System.out.println("Paramedic inicial agent " + this.getLocalName()
					+ "in pos" + my_x + " -" + my_y + " in floor" + my_f);

			if (my_y == yfinal && xfinal == my_x && zfinal == my_f) {

				return 0;

			}

			// option 1
			test.PosicioInicial(my_x, my_y, 1, my_f);
			// System.out.println("calcolo pos inicial");
			Path stepsPathFinal1 = test.PosicioFinal(xfinal, yfinal, 1, zfinal);
			// System.out.println(" calcolo path " + this.myAgent.getLocalName()
			// + "in pos1"+my_x + " -"+my_y +
			// "length"+stepsPathFinal1.getLength());

			// option 2
			test.PosicioInicial(my_x, my_y, 2, my_f);
			Path stepsPathFinal2 = test.PosicioFinal(xfinal, yfinal, 2, zfinal);

			// System.out.println("agent " + this.myAgent.getLocalName() +
			// "in pos2"+my_x + " -"+my_y);

			int distFinal = 10000;
			// Control if possible the second option
			if (stepsPathFinal2 != null) {
				distFinal = test.distanciaPesos(stepsPathFinal2);
				short_path = stepsPathFinal2;
				stepsPathFinal2.printPath();

				// Control if possible the first option
				if (stepsPathFinal1 != null) {

					int distPesosOp1 = test.distanciaPesos(stepsPathFinal1);
					if (distFinal > distPesosOp1) {
						distFinal = distPesosOp1;
						short_path = stepsPathFinal1;
						stepsPathFinal1.printPath();
					}
				}

			}
			return distFinal;
		} else
			return -1;
	}

	private Direction getNextStepDesti(Cell dest) {
		int destination_x = dest.getColumn();
		int destination_y = dest.getRow();

		/*
		 * if(my_x==destination_x && my_y==destination_y && my_f!=0) { return
		 * Direction.UPFLOOR; }
		 */
		if (my_x < destination_x && my_y == destination_y) {
			return Direction.RIGHT;

		}
		if (my_x > destination_x && my_y == destination_y) {
			return Direction.LEFT;
		}
		if (my_y < destination_y && my_x == destination_x) {
			return Direction.DOWN;
		}
		if (my_y > destination_y && my_x == destination_x) {
			return Direction.UP;
		}
		if (my_y > destination_y && my_x > destination_x) {
			return Direction.UPLEFT;
		}
		if (my_y < destination_y && my_x < destination_x) {
			return Direction.DOWNRIGHT;
		}

		if (my_x < destination_x && my_y > destination_y) {
			return Direction.UPRIGHT;
		}
		if (my_x > destination_x && my_y < destination_y) {
			return Direction.DOWNLEFT;
		}

		return Direction.NOP;

	}

	/**
	 * 
	 * @return the next Direction where harvester have go, this Direction is
	 *         calculated in short Path
	 */
	private Direction getNextStep() {
		int destination_x = short_path.getX(1);
		int destination_y = short_path.getY(1);

		/*
		 * if(my_x==destination_x && my_y==destination_y && my_f == 0) {
		 * 
		 * return Direction.OUT; }
		 */

		if (my_x < destination_x && my_y == destination_y) {
			return Direction.RIGHT;

		}
		if (my_x > destination_x && my_y == destination_y) {
			return Direction.LEFT;
		}
		if (my_y < destination_y && my_x == destination_x) {
			return Direction.DOWN;
		}
		if (my_y > destination_y && my_x == destination_x) {
			return Direction.UP;
		}
		if (my_y > destination_y && my_x > destination_x) {
			return Direction.UPLEFT;
		}
		if (my_y < destination_y && my_x < destination_x) {
			return Direction.DOWNRIGHT;
		}

		if (my_x < destination_x && my_y > destination_y) {
			return Direction.UPRIGHT;
		}
		if (my_x > destination_x && my_y < destination_y) {
			return Direction.DOWNLEFT;
		}

		return Direction.NOP;

	}

}